#include "kcompile.h"

#include "kglobal.h"

#include "ktoken.h"
#include "klexer.h"
#include "kparser.h"
#include "ksymtbl.h"
#include "klbltbl.h"
#include "ksemantics.h"
#include "kbytecode.h"
#include "kopcodes.h"
#include "kutils.h"
#include "kerror.h"

#include <cstdio>
#include <iostream>
#include <vector>

bool Compile(const char* src)
{
	using namespace std;

	Lexer lexer;
	Parser parser;

	try
	{
		lexer.Scan(src);
	}
	catch (KLexicalError &e)
	{
		cout << "Line " << e.line << ":" << endl;
		cout << "Lexical error: " << e.errMsg << endl;
		lexer.CleanUp();
		return false;
	}

	try
	{
		parser.Parse(lexer.GetTokenList());
	}
	catch (KSyntaxError &e)
	{
		cout << "Line " << e.line << ":" << endl;
		cout << "Syntax error: " << e.errMsg << endl;
		lexer.CleanUp();
		parser.CleanUp();
		return false;
	}

	vector<Command> cmds = parser.GetCommands();

#if _DEBUG
	cout << "===== REBUILDS CODE =====" << endl;
	for (vector<Command>::iterator it = cmds.begin(); it != cmds.end(); ++it)
		cout << *it;
	cout << endl;
#endif

	strPoolSize = lexer.GetStringPoolSize();
	strPool.Allocate(strPoolSize);

	try
	{
		Compile(cmds);
	}
	catch (KCompileError &e)
	{
		cout << "Line " << e.line << ":" << endl;
		cout << "Compile error: " << e.errMsg << endl;

		lexer.CleanUp();
		parser.CleanUp();
		CleanUpAll();

		return false;
	}

	lexer.CleanUp();
	parser.CleanUp();
	CleanUpAll();

	return true;
}

bool Compile(std::vector<Command> &cmds)
{
	programSize = 0;

	// Collects symbols and labels;
	// Performs type check;
	// Append string pool;
	// Calculate program size.
	AnalyseSemantics(cmds);

	// Initialize bytecode stream.
	bytecode.Init(programSize);

	// Emits bytecodes.
	std::vector<Command>::iterator it = cmds.begin();
	for (; it != cmds.end(); ++it)
		Emit(*it);

	bytecode << op_hlt;

#if _DEBUG
	const unsigned char *bc = bytecode.GetStream();
	const char *sp = strPool.GetPool();
	k_uint i = 0;

	using std::cout; using std::endl;

	cout << "===== COMPILATION INFO =====";

	cout << "\nProgram size: " << programSize << endl;
	lbltbl.DumpLabels();

	cout << "String pool:\n";
	for (const char *c = sp; i < strPoolSize; ++c, ++i)
		printf(" %02X", *c);

	cout << std::endl;

	cout << "Bytecode:\n"; i = 0;
	for (const unsigned char *c = bc; i < programSize; ++c, ++i)
		printf(" %02X", *c);

	cout << endl << endl;
#endif

	return true;
}

void Emit(const Command &cmd)
{
	if (hasDebugInfo && cmd.type > LABEL_LINE)
		bytecode << op_trace << cmd.line;

	switch (cmd.type)
	{
	case IO_IN_INT:
		// in
		// cvin
		// push
		bytecode << op_in << op_cvin << op_push;
		break;
	case IO_IN_REAL:
		// in
		// cvrn
		// push
		bytecode << op_in << op_cvrn << op_push;
		break;
	case IO_IN_STRING:
		// in
		// push
		bytecode << op_in << op_push;
		break;
	case IO_IN_VAR:
		// in
		// cvin/cvrn
		// stvar
		bytecode << op_in;
		switch (cmd.tokenList.front().v_int) //type code
		{
		case K_INT:
			bytecode << op_cvin;
			break;
		case K_REAL:
			bytecode << op_cvrn;
			break;
		}
		bytecode << op_stvar << symbols[cmd.identifier];
		break;
	case IO_OUT:
		// ... (expression)
		// out
		{
			std::vector<Token>::const_iterator it = cmd.tokenList.begin();
			for (; it->type != TOK_EOS; ++it)
				Emit(*it);
		}
		bytecode << op_out;
		break;
	case VAR_DECL:
		// ldi/ldr/ldes
		// new
		{
			switch (TranslateType(cmd.tokenList.front().type))
			{
			case K_INT:
				bytecode << op_ldi << (k_int)0;
				break;
			case K_REAL:
				bytecode << op_ldr << (k_real)0;
				break;
			case K_STRING:
				bytecode << op_ldes;
				break;
			}

			k_int h = symbols[cmd.identifier] = hash(cmd.identifier.c_str());
			bytecode << op_new << h;
		}
		break;
	case VAR_DECL_EX:
		// ... (expression)
		// [ cvi/cvr/cvs ]
		// new
		{
			std::vector<Token>::const_iterator it = cmd.tokenList.begin() + 1;
			for (; it->type != TOK_EOS; ++it)
				Emit(*it);

			k_int h = symbols[cmd.identifier] = hash(cmd.identifier.c_str());

			bool needCast = cmd.tokenList.back().v_int != 0;
			switch (TranslateType(cmd.tokenList.front().type)) //type code
			{
			case K_INT:
				if (needCast) bytecode << op_cvi;
				break;
			case K_REAL:
				if (needCast) bytecode << op_cvr;
				break;
			case K_STRING:
				if (needCast) bytecode << op_cvs;
				break;
			}

			bytecode << op_new << h;
		}
		break;
	case VAR_DEL:
		// free
		bytecode << op_free << symbols[cmd.identifier];
		break;
	case VAR_SET:
		// ... (expression)
		// [ cvi/cvr/cvs ]
		// stvar
		{
			std::vector<Token>::const_iterator it = cmd.tokenList.begin();
			for (; it->type != TOK_EOS; ++it)
				Emit(*it);
		}
		if (cmd.tokenList.back().v_int == 1)
		{
			switch ((KType)(&cmd.tokenList.back()-1)->v_int) //type code
			{
			case K_INT:
				bytecode << op_cvi;
				break;
			case K_REAL:
				bytecode << op_cvr;
				break;
			case K_STRING:
				bytecode << op_cvs;
				break;
			}
		}
		bytecode << op_stvar << symbols[cmd.identifier];
		break;
	case STACK_PUSH:
		// ... (expression)
		// push
		{
			std::vector<Token>::const_iterator it = cmd.tokenList.begin();
			for (; it->type != TOK_EOS; ++it)
				Emit(*it);
		}
		bytecode << op_push;
		break;
	case STACK_POP:
		// pop
		bytecode << op_pop;
		break;
	case STACK_POP_VAR:
		// smv
		// cvi/cvr/cvs
		// stvar
		bytecode << op_smv;
		switch (cmd.tokenList.back().v_int)
		{
		case K_INT:
			bytecode << op_cvi;
			break;
		case K_REAL:
			bytecode << op_cvr;
			break;
		case K_STRING:
			bytecode << op_cvs;
			break;
		}
		bytecode << op_stvar << symbols[cmd.identifier];
		break;
	case STACK_PEEK:
		// stf
		// cvi/cvr/cvs
		// stvar
		bytecode << op_stf;
		switch (cmd.tokenList.back().v_int)
		{
		case K_INT:
			bytecode << op_cvi;
			break;
		case K_REAL:
			bytecode << op_cvr;
			break;
		case K_STRING:
			bytecode << op_cvs;
			break;
		}
		bytecode << op_stvar << symbols[cmd.identifier];
		break;
	case STACK_SWAP:
		// swap
		bytecode << op_swap;
		break;
	case STACK_CLEAR:
		// sclr
		bytecode << op_sclr;
		break;
	case STACK_CLEAR_C:
		// sclrc
		bytecode << op_sclrc << cmd.tokenList.front().v_int;
		break;
	case TAPE_READ_INT:
		// trd/trdx
		// cvi
		// push
		if (cmd.tokenList.front().v_int == -1)
			bytecode << op_trd << op_cvi << op_push;
		else
			bytecode << op_trdx << cmd.tokenList.front().v_int << op_cvi << op_push;
		break;
	case TAPE_READ_REAL:
		// trd/trdx
		// cvr
		// push
		if (cmd.tokenList.front().v_int == -1)
			bytecode << op_trd << op_cvr << op_push;
		else
			bytecode << op_trdx << cmd.tokenList.front().v_int << op_cvr << op_push;
		break;
	case TAPE_READ_STRING:
		// trd/trdx
		// cvs
		// push
		if (cmd.tokenList.front().v_int == -1)
			bytecode << op_trd << op_cvs << op_push;
		else
			bytecode << op_trdx << cmd.tokenList.front().v_int << op_cvs << op_push;
		break;
	case TAPE_READ_VAR:
		// trd/trdx
		// cvi/cvr/cvs
		// stvar
		if (cmd.tokenList.front().v_int == -1)
			bytecode << op_trd;
		else
			bytecode << op_trdx << cmd.tokenList.front().v_int;
		switch (cmd.tokenList.back().v_int)
		{
		case K_INT:
			bytecode << op_cvi;
			break;
		case K_REAL:
			bytecode << op_cvr;
			break;
		case K_STRING:
			bytecode << op_cvs;
			break;
		}
		bytecode << op_stvar << symbols[cmd.identifier];
		break;
	case TAPE_WRITE:
		// ... (expression)
		// twr/twrx
		{
			std::vector<Token>::const_iterator it = cmd.tokenList.begin() + 1;
			for (; it->type != TOK_EOS; ++it)
				Emit(*it);
		}
		if (cmd.tokenList.front().v_int == -1)
			bytecode << op_twr;
		else
			bytecode << op_twrx << cmd.tokenList.front().v_int;
		break;
	case TAPE_NEXT:
		// tnxt
		bytecode << op_tnxt;
		break;
	case TAPE_PREV:
		// tnxt
		bytecode << op_tprv;
		break;
	case TAPE_MOVE:
		{
			std::vector<Token>::const_iterator it = cmd.tokenList.begin();
			for (; it->type != TOK_EOS; ++it)
				Emit(*it);
		}
		if (cmd.tokenList.back().v_int) // need casting
			bytecode << op_cvi;
		bytecode << op_tmov;
		break;
	case CTRL_GOTO:
		// jmp
		bytecode << op_jmp << lbltbl.GetLabelPosition(cmd.identifier, cmd.line);
		break;
	case CTRL_GOTO_IF:
		// ... (expression)
		// cjmp
		{
			std::vector<Token>::const_iterator it = cmd.tokenList.begin();
			for (; it->type != TOK_EOS; ++it)
				Emit(*it);
		}
		bytecode << op_cjmp << lbltbl.GetLabelPosition(cmd.identifier, cmd.line);
		break;
	case CTRL_CALL:
		// call
		bytecode << op_call << lbltbl.GetLabelPosition(cmd.identifier, cmd.line);
		break;
	case CTRL_CALL_IF:
		// ... (expression)
		// ccall
		{
			std::vector<Token>::const_iterator it = cmd.tokenList.begin();
			for (; it->type != TOK_EOS; ++it)
				Emit(*it);
		}
		bytecode << op_ccall << lbltbl.GetLabelPosition(cmd.identifier, cmd.line);
		break;
	case CTRL_RETURN:
		// ret
		bytecode << op_ret;
		break;
	case CTRL_END:
		// hlt
		bytecode << op_hlt;
		break;
	}
}

void Emit(const Token &tok)
{
	switch (tok.type)
	{
	case TOK_LITINT:
		bytecode << op_ldi << tok.v_int;
		break;
	case TOK_LITREAL:
		bytecode << op_ldr << tok.v_real;
		break;
	case TOK_LITSTRING:
		bytecode << op_lds << strPool.GetStringAddress(tok.v_string);
		break;
	case TOK_VAR:
		bytecode << op_ldvar << symbols[tok.v_string];
		break;
	case TOK_UPLUS:
		break;
	case TOK_UMINUS:
		bytecode << op_neg;
		break;
	case TOK_ADD:
		bytecode << op_add;
		break;
	case TOK_SUB:
		bytecode << op_sub;
		break;
	case TOK_MUL:
		bytecode << op_mul;
		break;
	case TOK_DIV:
		bytecode << op_div;
		break;
	case TOK_IDIV:
		bytecode << op_idiv;
		break;
	case TOK_MOD:
		bytecode << op_mod;
		break;
	case TOK_POW:
		bytecode << op_pow;
		break;
	case TOK_EQU:
		bytecode << op_equ;
		break;
	case TOK_NEQ:
		bytecode << op_neq;
		break;
	case TOK_LT:
		bytecode << op_lt;
		break;
	case TOK_LTE:
		bytecode << op_lte;
		break;
	case TOK_GT:
		bytecode << op_gt;
		break;
	case TOK_GTE:
		bytecode << op_gte;
		break;
	case TOK_CAT:
		bytecode << op_cat;
		break;
	case TOK_AND:
		bytecode << op_and;
		break;
	case TOK_OR:
		bytecode << op_or;
		break;
	case TOK_XOR:
		bytecode << op_xor;
		break;
	case TOK_NOT:
		bytecode << op_not;
		break;
	case TOK_CINT:
		bytecode << op_cvin; //tries to cast
		break;
	case TOK_CREAL:
		bytecode << op_cvrn; //tries to cast
		break;
	case TOK_FUNC:
		bytecode << op_func << tok.v_int;
		break;
	}
}

void CleanUpAll()
{
	symtbl.CleanUp();
	lbltbl.CleanUp();
	symbols.clear();
}
